/*************************************
Copyright 2011 Chinua Iloabachie, Pradnya Khutafale, Siyi Liu, Junichi Suzuki, and The University of Massachusetts, Boston (http://code.google.com/p/tabletuml/)


Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 
*************************************/


package touchMe.Util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StringReader;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import touchMe.baseClass.Constants;
import touchMe.baseClass.Diagram;
import touchMe.baseClass.Element;
import touchMe.baseClass.ElementType;
import touchMe.baseClass.RelElement;

import android.content.Context;
import android.util.Log;

/** this class contains all utilities like reading diagram (.uxf file), making copy of diagram etc
 * 
 * @author pkhobs
 *
 */
public class DiagramManager {
	private static DiagramManager diagramManager;
	private DiagramManager(){
		
	}
	public static DiagramManager getInstance(){
		if(diagramManager==null){
			diagramManager=new DiagramManager();
			getUmlDiagram().setZoomLevel(Constants.defaultZoomLevel);
		}
		return diagramManager;
		
	}
	public static Diagram getUmlDiagram() {
		return Diagram.getInstance();
	}

	public static void setUmlDiagram(Diagram umlDiagram) {
		Diagram.setInstance(umlDiagram);
	}
	/** Clear diagram
	 *  can be used for New diagram
	 */
	public static void clearInstance(){
		diagramManager=null;
		setUmlDiagram(null);
	}
	/** This metod reads the .uxf file and calls the XML parser
	 * and  creates a tochMe Diagram object
	 * @param context
	 * @param filePath
	 */
	public static void read(Context context,String filePath) {
		try {
			try {
				InputStream input = new FileInputStream(filePath);//context.getResources().openRawResource(	filePath);//R.raw.class1
				SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
				XMLContentHandler xmlhandler = new XMLContentHandler();
				parser.parse(input, xmlhandler);
				setUmlDiagram(xmlhandler.getDiagram());
			} catch (SAXException e) {
			}
		} catch (FileNotFoundException e) {
			Log.d(Constants.TAG,e.getMessage());
		} catch (SecurityException se) {
			Log.d(Constants.TAG,se.getMessage());
		} catch (Exception e1) {
			Log.d(Constants.TAG,e1.getMessage());
		}
	}
	/** This method takes a string and calls the XML parser
	 * and  creates a tochMe Diagram object
	 * @param filePath
	 */
	public static void parseString(String xmlcontent) {
		try {
			try {
				InputSource is = new InputSource(new StringReader(xmlcontent));
				SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
				XMLContentHandler xmlhandler = new XMLContentHandler();
				parser.parse(is, xmlhandler);
				setUmlDiagram(xmlhandler.getDiagram());
			} catch (SAXException e) {
				Log.d(Constants.TAG,"SAXE: "+e.getMessage());
			}
		} catch (SecurityException se) {
			Log.d(Constants.TAG,"SecEp: "+se.getMessage());
		} catch (Exception e1) {
			Log.d(Constants.TAG,"Exp: "+e1.getMessage());
		}
	}
	/**
	   * This method finds an element in diagram 
	   * @param clickedXLoc
	   * @param clickedYLoc
	   * @return element
	   */
	public static Element search4PanelAtt(float clickedXLoc, float clickedYLoc)
	{
		/*//commented by Pradnya
		Diagram uml;
		uml=Diagram.getInstance();
		
		
		for( Element e:uml.getElements())
		{
			if (e instanceof RelElement){
				RelElement re = (RelElement)e;
				float X=re.getFrom().getX()+re.getFrom().getW()/2;
				float Y=re.getFrom().getY()+re.getFrom().getH()/2;
				float toX=re.getTo().getX()+re.getTo().getW()/2;
				float toY=re.getTo().getY()+re.getTo().getH()/2;
				
				
				float slope=(toY-Y)/(toX-X);
				float b=Y-slope*X;
				float position=slope*clickedXLoc+b;
				if((position<clickedYLoc+5)&&(position>clickedYLoc-5))
					{return e;
					}
			 }
			else {
			float elementX=e.getX();
			float elementY=e.getY();
			float elementW=e.getW();
			float elementH=e.getH();
			// check if point at which finger was touched lies 
			// in range of element's boundary  and move all such elements
			if(clickedXLoc>= elementX && clickedXLoc<= elementX + elementW)
			{
				if(clickedYLoc>= elementY && clickedYLoc<= elementY + elementH)	
				{
					return e;
				}
			}
			
			}	*/
	//	}
		return null;
	}
	
  /**
   * This method moves elements/entities in diagram 
   * @param clickedXLoc
   * @param clickedYLoc
   * @param byX
   * @param byY
   */	
	public static boolean checkPosition(float clickedXLoc, float clickedYLoc)
	{
		Diagram uml;
		uml=Diagram.getInstance();
		
		for( Element e:uml.getElements())
		{
			if(e instanceof RelElement)
				continue;
			
			float elementX=e.getX();
			float elementY=e.getY();
			float elementW=e.getW();
			float elementH=e.getH();
			// check if point at which finger was touched lies 
			// in range of element's boundary  and move all such elements
			
			if(clickedXLoc>= elementX && clickedXLoc<= elementX + elementW)
			{
				if(clickedYLoc>= elementY && clickedYLoc<= elementY + elementH)	
				{
					return true;
				}
			}		
			
		}
		return false;
	}	
	public static boolean checkPositiontoadd(float clickedXLoc, float clickedYLoc,float tox, float toy)
	{
		Diagram uml;
		uml=Diagram.getInstance();
		Element e1 = null;
		Element e2=null;
		
		for( Element e:uml.getElements())
		{
			
			if (e instanceof RelElement)
				continue;
			float elementX=e.getX();
			float elementY=e.getY();
			float elementW=e.getW();
			float elementH=e.getH();
			
			// check if point at which finger was touched lies 
			// in range of element's boundary  and move all such elements
			if(clickedXLoc>= elementX && clickedXLoc<= elementX + elementW)
			{
				if(clickedYLoc>= elementY && clickedYLoc<= elementY + elementH)	
				{
					e1=e;
				}
			}
			
			if(tox>= elementX && tox<= elementX + elementW)
			{
				if(toy>= elementY && toy<= elementY + elementH)	
				{
					e2=e;
				}
			}
								
		}		
		if((e1==null)||(e2==null))
			return false;
		else			
		{
			RelElement erel = new RelElement(e1,e2);
			erel.setType(Constants.relationType);
			erel.setX(e1.getX()+e1.getW()/2);
			erel.setY(e1.getY()+e1.getH()/2);
			erel.setW(e2.getX()+e2.getW()/2);
			erel.setH(e2.getY()+e2.getH()/2);
			erel.setPanelAttributes(new StringBuffer(""));
			getUmlDiagram().getElements().add(erel);
			return true;
		}
	}
	public static Element moveOrSearch(float clickedXLoc, float clickedYLoc,float byX,float byY,
			boolean modeSearch)
	{
		Diagram uml;
		 
		uml=Diagram.getInstance();
		
		for( Element e:uml.getElements())
		{
			// check if point at which finger was touched lies 
			// in range of element's boundary  and move that element
			if(e.getPolicy()!=null){
			  if(e.getPolicy().moveOrsearch(clickedXLoc, clickedYLoc, byX, byY,modeSearch))
			  {  
				  if(!modeSearch)// that means MOVE mode
				    break;
			      if(modeSearch)
			    	  return e;
			  }//end if policy   
			}
		}//end for
		return null;
	}
	/** A method that modifies the diagram
	 * This method does not do anything if diagram is null!
	 * @param typeOrElemId is a type when mode is 1 otherwise it is an elementID
	 * @param propertyBoxString - a string from the propertybox
	 * @param mode - 1 implies add element, 2 implies update element in diagram 3 implies delete element
	 * @return
	 */
	public static boolean modifyDiagram(String typeOrElemId,String propertyBoxString, int mode){
		boolean state=false;
		int charWidth;
		int modelHeight;
		if(getUmlDiagram()==null)
			return false;
		try{
			if(typeOrElemId.equals(Constants.actorType)){
				charWidth=100;
				modelHeight=120;
			}
						
			else{
				String[] temp=propertyBoxString.split("\n");
				int numLines=temp.length;
				int lenLongest=0;
				for(String str:temp){
					int len=str.length();
					if(len>lenLongest)
						lenLongest=len;
				}
				charWidth=7*lenLongest;
				if(charWidth< 50) charWidth=50;
				modelHeight=14*numLines;
				if(modelHeight<40) modelHeight=40;
			}
			
			int xpad=200;
			int ypad=150;
			int numberPerLine=2;//number of elements to draw on a line
			if(mode==Constants.ADDELEMENT){//add element
				int numOfNewElements=0;
				for( Element e:getUmlDiagram().getElements()){
					if(e instanceof RelElement)
						continue;
					numOfNewElements++;
				}
				Element element=new Element();
				element.setType(typeOrElemId);
				element.setPanelAttributes(new StringBuffer(propertyBoxString));
				// added by Pradnya, again set right type in case of association
				element.setType(typeOrElemId);
				if(!(typeOrElemId.equals(Constants.relationType)))
				{ // if class/use case/actor do this				
				  element.setW(charWidth);
				  element.setX(10+(numOfNewElements % numberPerLine)*xpad);
				  element.setY(5+(numOfNewElements / numberPerLine)*ypad);
				  element.setH(modelHeight);
				}
				else
				{// if relation type
					element.setW(0);
					element.setX(0);
					element.setY(0);
					element.setH(0);
					
				}
				
				//watch out!
				getUmlDiagram().getElements().add(element);
				state=true;
			}
			else if (mode==Constants.UPDATEELEMENT){//edit element
				for( Element e:Diagram.getInstance().getElements())
				{
					if(e.toString().equals(typeOrElemId)){
						e.setPanelAttributes(new StringBuffer(propertyBoxString));
						if(e.getType()!=ElementType.ACTOR){
							e.setW(charWidth);
							e.setH(modelHeight);
						}
						return true;
					}
				}
			}
			else if (mode==Constants.DELETEELEMENT){//delete element
				for( Element e:Diagram.getInstance().getElements())
				{
					if(e.toString().equals(typeOrElemId)){
						Diagram.getInstance().getElements().remove(e);
						return true;
					}
				}
			}
		}catch(Exception e){
			Log.d("TouchMe","error in modifyDiagram:"+e.getMessage());
		}
		return state;
	}
}
